// 自动发现客户端版本,服务器版本集成在YU-UI中

#ifndef AUTO_FIND_H
#define AUTO_FIND_H

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <cstdint>
#include <sstream>
#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <mutex>
#include <cstdarg>
#include <iostream>
#include <iomanip>
#include <ctime>
#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
#include <map> //按键的顺序进行排序的，即按照键的比较运算符进行排序，默认情况下是升序排序
#include <unordered_map>

#if defined(_WIN32) || defined(_WIN64)

#include <winsock2.h>
#include <ws2tcpip.h>
#include <VersionHelpers.h>
#include <direct.h>
#include <sddl.h> // 需要链接 Secur32.lib
#include <fcntl.h>
#include <io.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Secur32.lib")

#define getStrStr   strtok_s
#define mkdir(path, mode) _mkdir(path)
#define mode_t int

#elif defined(__linux__)

#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <dirent.h>
#include <pwd.h>
typedef int     SOCKET;

#define INVALID_SOCKET -1
#define SOCKET_ERROR    -1

///函数转定义
#define closesocket    close
#define getStrStr      strtok_r

#else
#error "Unsupported operating system"
#endif

#if defined(_X86_64) || defined(__x86_64__) || defined(__x86_64)
// x86-64-specific code
#endif

#if defined(_ARM64_) || defined(__aarch64__) || defined(__aarch64)
// ARM64-specific code
#endif

#include "version.h"
#include "cJSON.h"
#include "log.h"
#include "ThreadPool.h"

#define AUTO_PORT   6000
#define AUTO_SIZE   1024
#define AUTO_FLAG   "zhouyu::"
#define MAX_FILE_SIZE (16 * 1024 * 1024) // 16MB

#define SEND_PACK(root)     SKPacket *packet = (struct SKPacket *)malloc(sizeof(struct SKPacket)); \
auto data = cJSON_PrintUnformatted(root);                    \
packet->data = (uint8_t*)data;\
packet->size = strlen(data);  \
send_queue.Push(packet)

///保留每项测试的运行状态
typedef std::unordered_map<std::string, bool> TestStatus;
extern TestStatus testStatus;

template<typename T>
class ThreadSafeQueue {
public:
    ThreadSafeQueue() {}

    ThreadSafeQueue(ThreadSafeQueue const &other) {
        std::lock_guard <std::mutex> lk(other.m_mutex);
        m_dataQueue = other.m_dataQueue;
    }

    void Push(T new_value) {
        std::lock_guard <std::mutex> lk(m_mutex);
        m_dataQueue.push(new_value);
        m_condVar.notify_one();
    }

    T Pop() {
        std::unique_lock <std::mutex> lk(m_mutex);
        if (Empty())
            return nullptr;
        T res = m_dataQueue.front();
        m_dataQueue.pop();
        return res;
    }

    bool Empty() const { return m_dataQueue.empty(); }

    size_t Size() {
        std::unique_lock <std::mutex> lk(m_mutex);
        return m_dataQueue.size();
    }

private:
    mutable std::mutex m_mutex;
    std::queue <T> m_dataQueue;
    std::condition_variable m_condVar;
};

typedef struct SKPacket {
    uint8_t *data;           ///< 数据指针,时间需要处理的json数据
    size_t   size;           ///< 数据大小
    int64_t  pts;            ///< 显示时间戳,用于验证数据包的有效性和完整性
} SKPacket;

extern ThreadSafeQueue<SKPacket *>  send_queue;

///指令交互
///一般还是由服务器端发起
enum SocketCMD {
    ///获取设备信息
    hello,
    ///ping
    ping,
    ///获取设备日志
    logs,
    ///执行自定义指令
    cmd,
    ///自动测试
    test,
    ///文件传输
    file,
    ///传输错误
    error
};

class CMDHandle {

public:
    void handleError(const char *err,bool test_over = true);
    void handleHello(cJSON *id);
    ///返回所有测试日志
    void handleLog();
    void handlePing(std::string id);
    void handleDmesg(std::string file);
    void handleCMD(std::string cmd);
    void handleFiles(cJSON *paths,int dir);
    void handleTest(cJSON *root);
};

class AutoFind {
public:
    AutoFind();
    ~AutoFind();
    bool getInitOk(){return init_ok;}
    void setHost(char *host);
    void setPort(char *port);
    void setExit(bool exit){
        this->exit = exit;
    }
    bool getExit(){return exit;}
    bool connect();
    int receiveLoop();
    bool sendData(const char *buf, int32_t bufSize);
    bool handleCMD(cJSON *root,CMDHandle *cmdHandle);
    std::string getServer(){
        return host + ":" + std::to_string(port);
    };
private:

    bool recvPacket(SKPacket *packet);
    bool pushPacket(SKPacket *packet);
    void resetPacket(SKPacket *packet);
    bool newPacket(SKPacket *packet,int len);

    int32_t recvData(uint8_t *buf, int32_t bufSize);
    SOCKET socket_;
    bool init_ok;
    std::string host;
    int port;
    bool exit;
    FILE* file;
};

///收发线程函数
void SendAndRecvThread(AutoFind *autoFind);

static inline std::string cJSONArray_toString(cJSON *array){
    if(array->type!=cJSON_Array){
        return "";
    }

    int size = cJSON_GetArraySize(array);
    char *str = (char*)malloc(size+1);
    for (int i=0;i<size;i++) {
        str[i]= cJSON_GetArrayItem(array,i)->valueint;
    }
    str[size] = '\0';
    std::string utf8_str(
            reinterpret_cast<const char*>(str),
            size
    );
    free(str);
    return utf8_str;
}
#endif
